#include "Functions.h"

short int ReadFile(char *filename) {
    struct stat buf;
    char buffer_aux[BLOCKSIZE+1];
    ssize_t nbytes;
    int fd;

    fd = open(filename, O_RDONLY);
    if(fd == -1) {
        printf("Cannot open the file!\n");
        return 1;
    }

    file = malloc(sizeof(FileStruct));
    fstat(fd, &buf);
    file->size = (unsigned long int)buf.st_size;
    file->content = malloc(file->size*sizeof(char));
    while((nbytes = read(fd, buffer_aux, BLOCKSIZE)) > 0) {     //le todo o conteudo do arquivo
        strncat(file->content, buffer_aux, nbytes);
    }

    return 0;
}

short int AllocateData(int n_procs) {
    char *p;    //ponteiro que ira percorrer todo o conteudo do arquivo na memoria
    unsigned register int i, j;

    p = strtok(file->content, "\n");    //primeiramente le os parametros do metodo jacobi
    sscanf(p, "%d", &file->J_ORDER);
    p = strtok(NULL, "\n");
    sscanf(p, "%d", &file->J_ROW_TEST);
    p = strtok(NULL, "\n");
    sscanf(p, "%lf", &file->J_ERROR);
    p = strtok(NULL, "\n");
    sscanf(p, "%d", &file->J_ITE_MAX);
    if(file->J_ORDER == 0 || file->J_ROW_TEST < 0 || file->J_ROW_TEST >= file->J_ORDER) {
        printf("Error while loading parameters of the file!\n");
        return 1;
    }
    file->vectorB = malloc(file->J_ORDER*sizeof(double));   //aloca memoria para o vetorB
    file->matrizA = malloc(file->J_ORDER*sizeof(double*));  //aloca memoria para a matrizA
    for(i = 0; i < file->J_ORDER; i++) {
        file->matrizA[i] = malloc(file->J_ORDER*sizeof(double));
    }

    for(i = 0; i < file->J_ORDER; i++) {    //le os valores da matrizA
        for(j = 0; j < file->J_ORDER; j++) {
            p = strtok(NULL, " ");
            sscanf(p, "%lf", &file->matrizA[i][j]);
        }
    }

    for(i = 0; i < file->J_ORDER; i++) {    //le os valores do vetorB
        p = strtok(NULL, "\n");
        sscanf(p, "%lf", &file->vectorB[i]);
    }

	recvcount = malloc(n_procs*sizeof(int));
	displs = malloc(n_procs*sizeof(int));
	recvcount[0] = 0;
	displs[0] = 0;
	ind = 0;

    return 0;
}

short int ManipulateData() {
    unsigned register int i, j;
    double aux;

    file->line_test = malloc((file->J_ORDER+1)*sizeof(double));
    for(i = 0; i < file->J_ORDER; i++) {        //armazena a linha da matrizA a ser testada (o indice foi passado como parametro no arquivo lido), inclusive o valor correspondente no vetorB
        file->line_test[i] = file->matrizA[file->J_ROW_TEST][i];
    }
    file->line_test[i] = file->vectorB[file->J_ROW_TEST];

    for(i = 0; i < file->J_ORDER; i++) {    //divide toda a matrizA e o vetorB pela diagonal correspondente
        if((aux = file->matrizA[i][i]) == 0) {
            printf("MatrizA[%d][%d] = 0, invalido!", i, i);
            return 1;
        }
        for(j = 0; j < file->J_ORDER; j++) {
            file->matrizA[i][j] = file->matrizA[i][j]/aux;
        }
        file->vectorB[i] = file->vectorB[i]/aux;
    }

    return 0;
}

void SetParameters(int n_procs) {
    file->LINES_PER_PROC = file->J_ORDER/n_procs;   //determina o numero medio de linhas da matrizA que vao ser distribuidas para cada no
    file->rest = file->J_ORDER%n_procs;     //se a divisao nao for exata, entao repassa essa sobra entre os nos
    file->count = 0;    //comeca a distribuicao a partir da primeira linha da matrizA
}

int GetAmountLines() {
    int lines;

    lines = file->LINES_PER_PROC+(file->rest && 1);     //determina a quantidade de linhas da matrizA para determinado no
    if(file->rest)
        file->rest--;
    return lines;
}

void GetLines(double *lines, int amount, int *pos) {
    unsigned register int i, j;
    int l;
	int aux;

	recvcount[ind] = amount;
	displs[ind] = file->count;
	ind++;
	l = 0;
    for(i = file->count; i < file->count+amount; i++) {     //preenche a matriz com os valores correspondentes a linhas da matrizA
        for(j = 0; j < file->J_ORDER; j++) {
            lines[l++] = file->matrizA[i][j];
        }
    }
    *pos = file->count;
    file->count += amount;      //incrementa variavel da estrutura para que o proximo no obtenha as proximas linhas da matrizA
}

void CalculateError(double *xk, double *xk_1, double *error) {
    unsigned register int i;
    double max_k = 0, max_k1 = 0, *dif;

    dif = malloc(file->J_ORDER*sizeof(double));
    for(i = 0; i < file->J_ORDER; i++) {
        dif[i] = fabs(xk_1[i]-xk[i]);
        if(xk_1[i] > max_k1)
            max_k1 = xk_1[i];
        if(dif[i] > max_k)
            max_k = dif[i];
    }
    *error = max_k/max_k1;
}

void ShowElapsedTime(struct timeval *t1, struct timeval *t2) {
	double segundos;
	double milisegundos;

	double elapsed_sec = t2->tv_sec - t1->tv_sec;
	double elapsed_usec = t2->tv_usec - t1->tv_usec;

	segundos = elapsed_sec;
	milisegundos = elapsed_usec/1e6;

	printf("Tempo de execucao: ");
    printf("%.2lfs\n", segundos+milisegundos);

}

